/*
 * Copyright 2023, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include "accelerant.h"

#include <stdlib.h>
#include <string.h>

#include <compute_display_timing.h>
#include <create_display_modes.h>

// 支持的颜色空间列表
static const color_space kSupportedColorSpaces[] = {
    B_RGB32,
    B_RGB24,
    B_RGB16,
    B_RGB15,
    B_CMAP8};

// 检查模式是否支持
static bool
is_mode_supported(display_mode *mode)
{
    // 检查分辨率是否在合理范围内
    if (mode->virtual_width < 640 || mode->virtual_width > 1920 ||
        mode->virtual_height < 480 || mode->virtual_height > 1200)
        return false;

    // 检查颜色空间是否支持
    for (uint32 i = 0; i < sizeof(kSupportedColorSpaces) / sizeof(color_space); i++)
    {
        if (mode->space == kSupportedColorSpaces[i])
            return true;
    }

    return false;
}

/*!  Creates the initial mode list of the primary accelerant.
    It's called from ls7a2000_init_accelerant().
*/
status_t
create_mode_list(void)
{
    // 检查是否已经有模式列表
    if (gInfo->mode_list != NULL)
    {
        return B_OK;
    }

    // 初始模式列表
    display_mode *initialModes = NULL;
    uint32 initialModesCount = 0;

    // 如果有EDID信息，使用它来创建模式列表
    if (gInfo->shared_info->has_edid)
    {
        TRACE("Using EDID information to create mode list\n");

        // 创建模式列表区域
        gInfo->mode_list_area = create_display_modes("ls7a2000 modes",
                                                     (edid1_info *)gInfo->shared_info->edid,
                                                     NULL, 0,
                                                     kSupportedColorSpaces,
                                                     sizeof(kSupportedColorSpaces) / sizeof(color_space),
                                                     is_mode_supported,
                                                     &gInfo->mode_list,
                                                     &gInfo->shared_info->mode_count);
    }
    else
    {
        TRACE("No EDID information, using predefined modes\n");

        // 使用预定义的模式列表
        initialModesCount = 0;
        while (kSupportedDisplayModes[initialModesCount].width != 0)
            initialModesCount++;

        initialModes = (display_mode *)malloc(sizeof(display_mode) * initialModesCount);
        if (initialModes == NULL)
            return B_NO_MEMORY;

        // 转换预定义模式到display_mode格式
        for (uint32 i = 0; i < initialModesCount; i++)
        {
            const ls7a2000_display_mode &srcMode = kSupportedDisplayModes[i];
            display_mode &destMode = initialModes[i];

            // 计算显示时序
            compute_display_timing(srcMode.width, srcMode.height, srcMode.refresh_rate, false,
                                   &destMode.timing);

            // 填充模式信息
            destMode.space = srcMode.color_space;
            destMode.virtual_width = srcMode.width;
            destMode.virtual_height = srcMode.height;
            destMode.h_display_start = 0;
            destMode.v_display_start = 0;
            destMode.flags = B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_8_BIT_DAC | B_DPMS;
        }

        // 创建模式列表区域
        gInfo->mode_list_area = create_display_modes("ls7a2000 modes",
                                                     NULL,
                                                     initialModes, initialModesCount,
                                                     kSupportedColorSpaces,
                                                     sizeof(kSupportedColorSpaces) / sizeof(color_space),
                                                     is_mode_supported,
                                                     &gInfo->mode_list,
                                                     &gInfo->shared_info->mode_count);

        free(initialModes);
    }

    if (gInfo->mode_list_area < 0)
        return gInfo->mode_list_area;

    gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
    return B_OK;
}

//  #pragma mark - public accelerant functions

uint32
ls7a2000_accelerant_mode_count(void)
{
    TRACE("ls7a2000_accelerant_mode_count() = %d\n",
          gInfo->shared_info->mode_count);
    return gInfo->shared_info->mode_count;
}

status_t
ls7a2000_get_mode_list(display_mode *modeList)
{
    TRACE("ls7a2000_get_mode_list()\n");
    memcpy(modeList, gInfo->mode_list,
           gInfo->shared_info->mode_count * sizeof(display_mode));
    return B_OK;
}

status_t
ls7a2000_set_display_mode(display_mode *mode)
{
    TRACE("ls7a2000_set_display_mode()\n");

    // 检查模式是否有效
    if (mode == NULL)
        return B_BAD_VALUE;

    // 查找匹配的模式
    uint32 modeIndex;
    for (modeIndex = 0; modeIndex < gInfo->shared_info->mode_count; modeIndex++)
    {
        if (memcmp(&gInfo->mode_list[modeIndex], mode, sizeof(display_mode)) == 0)
            break;
    }

    if (modeIndex >= gInfo->shared_info->mode_count)
        return B_BAD_VALUE;

    // 设置当前模式
    ls7a2000_display_mode displayMode;
    displayMode.width = mode->virtual_width;
    displayMode.height = mode->virtual_height;
    displayMode.refresh_rate = (uint16)(mode->timing.pixel_clock * 1000 /
                                        (mode->timing.h_total * mode->timing.v_total));
    displayMode.bits_per_pixel = (mode->space == B_CMAP8) ? 8 : (mode->space == B_RGB15) ? 15
                                                            : (mode->space == B_RGB16)   ? 16
                                                            : (mode->space == B_RGB24)   ? 24
                                                                                         : 32;
    displayMode.color_space = mode->space;

    // 设置标志位
    if (displayMode.width == 640 && displayMode.height == 480)
        displayMode.flags = DISPLAY_MODE_640_480_60HZ;
    else if (displayMode.width == 800 && displayMode.height == 600)
        displayMode.flags = DISPLAY_MODE_800_600_60HZ;
    else if (displayMode.width == 1024 && displayMode.height == 768)
        displayMode.flags = DISPLAY_MODE_1024_768_60HZ;
    else if (displayMode.width == 1280 && displayMode.height == 1024)
        displayMode.flags = DISPLAY_MODE_1280_1024_60HZ;
    else if (displayMode.width == 1440 && displayMode.height == 900)
        displayMode.flags = DISPLAY_MODE_1440_900_60HZ;
    else if (displayMode.width == 1680 && displayMode.height == 1050)
        displayMode.flags = DISPLAY_MODE_1680_1050_60HZ;
    else if (displayMode.width == 1920 && displayMode.height == 1080)
        displayMode.flags = DISPLAY_MODE_1920_1080_60HZ;
    else if (displayMode.width == 1920 && displayMode.height == 1200)
        displayMode.flags = DISPLAY_MODE_1920_1200_60HZ;
    else
        displayMode.flags = DISPLAY_MODE_1024_768_60HZ; // 默认模式

    // 通过IOCTL设置模式
    status_t status = ioctl(gInfo->device, LS7A2000_SET_DISPLAY_MODE, &displayMode, sizeof(displayMode));
    if (status != B_OK)
        return status;

    // 更新当前模式
    gInfo->shared_info->current_mode = *mode;
    gInfo->current_mode = modeIndex;

    return B_OK;
}

status_t
ls7a2000_get_display_mode(display_mode *currentMode)
{
    TRACE("ls7a2000_get_display_mode()\n");

    if (currentMode == NULL)
        return B_BAD_VALUE;

    *currentMode = gInfo->shared_info->current_mode;
    return B_OK;
}

status_t
ls7a2000_get_edid_info(void *info, size_t size, uint32 *_version)
{
    TRACE("ls7a2000_get_edid_info()\n");

    if (!gInfo->shared_info->has_edid)
        return B_ERROR;
    if (size < sizeof(struct edid1_info))
        return B_BUFFER_OVERFLOW;

    // 复制EDID数据
    memcpy(info, gInfo->shared_info->edid, sizeof(struct edid1_info));
    *_version = EDID_VERSION_1;
    return B_OK;
}

status_t
ls7a2000_get_pixel_clock_limits(display_mode *mode, uint32 *low, uint32 *high)
{
    TRACE("ls7a2000_get_pixel_clock_limits()\n");

    if (low != NULL)
        *low = 0;

    if (high != NULL)
        *high = 200000; // 200 MHz

    return B_OK;
}